//package com.tianji.promotion.service.impl;
//
//import cn.hutool.core.bean.copier.CopyOptions;
//import com.baomidou.mybatisplus.core.metadata.OrderItem;
//import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
//import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
//import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
//import com.tianji.common.constants.MqConstants;
//import com.tianji.common.domain.dto.PageDTO;
//import com.tianji.common.exceptions.BadRequestException;
//import com.tianji.common.exceptions.BizIllegalException;
//import com.tianji.common.utils.BeanUtils;
//import com.tianji.common.utils.CollUtils;
//import com.tianji.common.utils.StringUtils;
//import com.tianji.common.utils.UserContext;
//import com.tianji.promotion.constants.PromotionConstants;
//import com.tianji.promotion.discount.Discount;
//import com.tianji.promotion.discount.DiscountStrategy;
//import com.tianji.promotion.domain.dto.CouponDiscountDTO;
//import com.tianji.promotion.domain.dto.OrderCourseDTO;
//import com.tianji.promotion.domain.dto.UserCouponDTO;
//import com.tianji.promotion.domain.po.Coupon;
//import com.tianji.promotion.domain.po.CouponScope;
//import com.tianji.promotion.domain.po.ExchangeCode;
//import com.tianji.promotion.domain.po.UserCoupon;
//import com.tianji.promotion.domain.query.CouponQuery;
//import com.tianji.promotion.domain.vo.CouponVO;
//import com.tianji.promotion.enums.CouponStatus;
//import com.tianji.promotion.enums.ExchangeCodeStatus;
//import com.tianji.promotion.enums.MyLockType;
//import com.tianji.promotion.mapper.CouponMapper;
//import com.tianji.promotion.mapper.UserCouponMapper;
//import com.tianji.promotion.service.ICouponScopeService;
//import com.tianji.promotion.service.IExchangeCodeService;
//import com.tianji.promotion.service.IUserCouponService;
//import com.tianji.promotion.utils.CodeUtil;
//import com.tianji.promotion.utils.MyLock;
//import com.tianji.promotion.utils.MyLockStrategy;
//import com.tianji.promotion.utils.PermuteUtil;
//import lombok.RequiredArgsConstructor;
//import lombok.extern.slf4j.Slf4j;
//import org.redisson.api.RedissonClient;
//import org.springframework.aop.framework.AopContext;
//import org.springframework.core.io.ClassPathResource;
//import org.springframework.data.redis.core.StringRedisTemplate;
//import org.springframework.data.redis.core.script.RedisScript;
//import org.springframework.stereotype.Service;
//import org.springframework.transaction.annotation.Transactional;
//
//import java.time.LocalDateTime;
//import java.util.*;
//import java.util.concurrent.CompletableFuture;
//import java.util.concurrent.CountDownLatch;
//import java.util.concurrent.Executor;
//import java.util.concurrent.TimeUnit;
//import java.util.function.Consumer;
//import java.util.function.Predicate;
//import java.util.function.Supplier;
//import java.util.stream.Collectors;
//
///**
// * <p>
// * v5 基于Mq异步实现 用户领取优惠券的记录，是真正使用的优惠券信息 服务实现类
// * </p>
// *
// * @author friday
// * @since 2024-01-16
// */
//@Slf4j
//@Service
//@RequiredArgsConstructor
//public class UserCouponMqServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {
//
//    private final CouponMapper couponMapper;
//    private final IExchangeCodeService exchangeCodeService;
//    private final StringRedisTemplate redisTemplate;
//    private final RedissonClient redissonClient;
//    private final RabbitMqHelper rabbitMqHelper;
//    private final ICouponScopeService couponScopeService;
//    private final Executor calculateSolutionExecutor;
//
//    //用户领取优惠券
//    @Override
//    //@Transactional
//    // 分布式锁 可以对 优惠券加锁 ，receiveCoupon中都改造为查询redis，性能不会降低，控制并发 ,因为校验库存和修改优惠券库存  -1不是原子性的，所以加锁
//    @MyLock(name = "lock:coupon:uid:#{couponId}")
//    public void receiveCoupon(Long couponId) {
//        //1.根据id查询优惠券  做相关校验
//        if (couponId == null){
//            throw new BadRequestException("非法参数");
//        }
////        Coupon coupon = couponMapper.selectById(couponId);
//        //从redis中获取优惠券信息
//        Coupon coupon = queryCouponByCache(couponId);
//        if (coupon == null){
//            throw new BadRequestException("该优惠券不存在");
//        }
////        if (coupon.getStatus() != CouponStatus.ISSUING){
////            throw new BadRequestException("该优惠券未发放");
////        }
//        LocalDateTime now = LocalDateTime.now();
//        if (now.isBefore(coupon.getIssueBeginTime()) ||now.isAfter(coupon.getIssueEndTime())){
//            throw new BadRequestException("该优惠券已过期或未开始发放");
//        }
////        if (coupon.getTotalNum() <= 0 || coupon.getIssueNum() >= coupon.getTotalNum()){
//        if (coupon.getTotalNum() <= 0){     //每次领取，totalNum就减一，totalNum就相当于剩余数量
//            throw new BadRequestException("该优惠券库存不足");
//        }
//        Long userId = UserContext.getUser();
//
//        //方式5.Mq异步实现
//        //统计已领取数量，通过redis中获取
//        String key = PromotionConstants.USER_COUPON_CACHE_KEY_PREFIX + couponId; //prs:user:coupon:优惠券id
//        //很多线程同时进来，每次都 +1 ，redis里面的 value 会超出limit，但是后面increment > coupon.getUserLimit()会抛出异常，用户真正领的数量还是1
//        Long increment = redisTemplate.opsForHash().increment(key, userId.toString(), 1);
//        //校验是否超过限领数量
//        if (increment > coupon.getUserLimit()){ //因为每次都是先+1，所以校验的时候不能有等于
//            throw new BizIllegalException("该优惠券已达到领取上限");
//        }
//        //修改优惠券库存  -1
//        String couponKey = PromotionConstants.COUPON_CACHE_KEY_PREFIX + couponId;
//        redisTemplate.opsForHash().increment(couponKey, "totalNum", -1);
//
//        //发送消息到 mq 校验通过用户可以领券了 消息内容为 userId和 couponId
//        UserCouponDTO msg = new UserCouponDTO();
//        msg.setCouponId(couponId);
//        msg.setUserId(userId);
//        rabbitMqHelper.send(
//                MqConstants.Exchange.PROMOTION_EXCHANGE,
//                MqConstants.Key.COUPON_RECEIVE,
//                msg);
//
//    }
//
//    /**
//     * 从redis中获取优惠券信息
//     * @param couponId
//     * @return
//     */
//    private Coupon queryCouponByCache(Long couponId) {
//        //1.拼接key
//        String key = PromotionConstants.COUPON_CACHE_KEY_PREFIX + couponId;
//        //从redis获取数据
//        Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);//得到该key下的所有数据，相当于redis中的getall
//        //2.将map转换成coupon对象
//        Coupon coupon = BeanUtils.mapToBean(entries, Coupon.class, false, CopyOptions.create());
//        return coupon;
//    }
//
//    //兑换码兑换优惠券
//    @Override
//    @Transactional
//    public void exchangeCoupon(String code) {
//        //1.校验code是否为空
//        if (StringUtils.isBlank(code)){
//            throw new BadRequestException("非法参数");
//        }
//        //2.解析兑换码得到自增id
//        long serialNum = CodeUtil.parseCode(code); //解析兑换码得到自增id
//        //3.判断兑换码是否已兑换 采用redis的bitmap结构 setbit key offset 1 如果方法返回true代表兑换码已兑换
//        boolean result = exchangeCodeService.updateExchangeCodeMark(serialNum,true);
//        if (result){
//            throw new BizIllegalException("该兑换码已兑换");
//        }
//        try {
//            //4.判断兑换码是否存在 根据自增id查询 主键查询
//            ExchangeCode exchangeCode = exchangeCodeService.getById(serialNum);
//            if (exchangeCode == null){
//                throw new BizIllegalException("该兑换码不存在");
//            }
//            //5.判断是否过期
//            LocalDateTime now = LocalDateTime.now();
//            LocalDateTime expiredTime = exchangeCode.getExpiredTime();
//            if (now.isAfter(expiredTime)){
//                throw new BizIllegalException("该兑换码已过期");
//            }
//            //校验并生成用户券
//            Long userId = UserContext.getUser();
//            //查询优惠券信息
//            Coupon coupon = couponMapper.selectById(exchangeCode.getExchangeTargetId());
//            if (coupon == null){
//                throw new BizIllegalException("该优惠券不存在");
//            }
//            //6.判断是否超出限领数量  7.优惠券已发放数量 +1  8.生成用户券 9.更新兑换码状态
//            checkAndCreateUserCoupon(userId,coupon,serialNum);
//
//        }catch (Exception e){
//            //10.将兑换码的状态重置 （如果事务回滚，只是操作数db的事务回滚，操作redis需要手动回滚）
//            exchangeCodeService.updateExchangeCodeMark(serialNum,false);
//            throw e;
//        }
//    }
//
//    @Transactional
//    @MyLock(name = "lock:coupon:uid:#{userId}",
//            lockType = MyLockType.RE_ENTRANT_LOCK,
//            lockStrategy = MyLockStrategy.FAIL_FAST)
//    public void checkAndCreateUserCoupon(Long userId, Coupon coupon, Long serialNum) {
//        //Long类型-128~127之间是同一个对象    超过该区问则是不同的对象
//        //Long.toString方法底层是new String,所以还是不同的对象
//        //Long.toString.intern() intern方法是强制从常量池中取字符串
//        //1.获取当前用户  对该优惠券 已领数量  user_coupon   条件user_id coupon_id  数量
//        Integer count = this.lambdaQuery()
//                .eq(UserCoupon::getUserId, userId)
//                .eq(UserCoupon::getCouponId, coupon.getId())
//                .count();
//        if (count != null && count >= coupon.getUserLimit()){
//            throw new BadRequestException("该优惠券已达到领取上限");
//        }
//        //2.优惠券的已发放数量 +1
//        int num = couponMapper.incrIssueNum(coupon.getId()); //采用这种方式，烤炉并发控制，后期仍需修改
//        if (num == 0){
//            throw new BizIllegalException("该优惠券已发放完");
//        }
//        //3.生成用户券
//        saveUserCoupon(userId,coupon);
//
//        //4，修改兑换码状态
//        if (serialNum != null){
//            //修改兑换码状态
//            exchangeCodeService.lambdaUpdate()
//                    .set(ExchangeCode::getStatus, ExchangeCodeStatus.USED)
//                    .set(ExchangeCode::getUserId, userId)
//                    .eq(ExchangeCode::getId, serialNum)
//                    .update();
//        }
//    }
//
//    /**
//     * Mq中的用户领券业务
//     * @param msg
//     */
////    @MyLock(name = "lock:coupon:uid:#{userId}",
////            lockType = MyLockType.RE_ENTRANT_LOCK,
////            lockStrategy = MyLockStrategy.FAIL_FAST)
//    @Transactional
//    @Override
//    public void checkAndCreateUserCouponNew(UserCouponDTO msg) {
//        //1.从db查询优惠券信息
//        Coupon coupon = couponMapper.selectById(msg.getCouponId());
//        if (coupon == null){
//            return; //这里不能抛异常，就会重试，还是不对，应该直接return
//        }
//        //2.优惠券的已发放数量 +1
//        int num = couponMapper.incrIssueNum(coupon.getId());//采用这种方式，考虑并发控制，后期仍需修改
//        if (num == 0){
////            throw new BizIllegalException("该优惠券已发放完");
//            return;
//        }
//        //3.生成用户券
//        saveUserCoupon(msg.getUserId(),coupon);
//    }
//
//    //分页查询我的优惠券接口
//    @Override
//    public PageDTO<CouponVO> queryMyCouponPage(CouponQuery query) {
//        //1.获取当前用户
//        Long userId = UserContext.getUser();
//        //2.分页查询用户券
//        //WHERE (user_id = ? AND status = ?) ORDER BY term_end_time ASC 以优惠券有效期结束时间 升序
//        Page<UserCoupon> page = lambdaQuery()
//                .eq(UserCoupon::getUserId, userId)
//                .eq(UserCoupon::getStatus, query.getStatus())
//                .page(query.toMpPage(new OrderItem("term_end_time", true)));
//        List<UserCoupon> records = page.getRecords();
//        if (CollUtils.isEmpty(records)){
//            return PageDTO.empty(page);
//        }
//        //3.获取优惠券详细信息
//        //3.1获取用户关联的优惠券id
//        Set<Long> couponIds = records.stream().map(UserCoupon::getCouponId).collect(Collectors.toSet());
//        //3.2查询 coupon表
//        List<Coupon> coupons = couponMapper.selectBatchIds(couponIds);
//        //4.封装vo返回
//        return PageDTO.of(page,BeanUtils.copyList(coupons,CouponVO.class));
//    }
//
//    //查询我的优惠券可用方案
//    @Override
//    public List<CouponDiscountDTO> findDiscountSolution(List<OrderCourseDTO> courses) {
//        //1.查询当前用户可用的优惠券 coupon和user_coupon 条件 userId status=1(可用) 优惠券的规则 优惠券id 用户券id
//        List<Coupon> coupons = getBaseMapper().queryMyCoupons(UserContext.getUser());
//        if (CollUtils.isEmpty(coupons)){
//            return CollUtils.emptyList();
//        }
//        //测试
//        log.debug("用户的优惠券共有 {}张",coupons.size());
//        for (Coupon coupon : coupons) {
//            log.debug("优惠券：{}, {}",
//                    DiscountStrategy.getDiscount(coupon.getDiscountType()).getRule(coupon),
//                    coupon);
//        }
//        //2.初筛
//        //2.1计算订单的总金额 对courses中的price累加
//        int totalAmount = courses.stream().mapToInt(OrderCourseDTO::getPrice).sum();//订单的总金额
//        log.debug("订单的总金额为：{}",totalAmount);
//        //2.2 校验优惠券是否可用
//        //方法1：循环写法
//        /*List<Coupon> availableCoupons = new ArrayList<>();
//        for (Coupon coupon : coupons) {
//            boolean flag = DiscountStrategy.getDiscount(coupon.getDiscountType()).canUse(totalAmount, coupon);
//            if (flag){
//                availableCoupons.add(coupon);
//            }
//        }*/
//        //方法2：stream流
//        /*List<Coupon> availableCoupons = coupons.stream().filter(new Predicate<Coupon>() {
//            @Override
//            public boolean test(Coupon coupon) {
//                return DiscountStrategy.getDiscount(coupon.getDiscountType()).canUse(totalAmount, coupon);
//            }
//        }).collect(Collectors.toList());*/
//        //方法3：stream转为lambda
//        List<Coupon> availableCoupons = coupons.stream()
//                .filter(coupon -> DiscountStrategy.getDiscount(coupon.getDiscountType()).canUse(totalAmount, coupon))
//                .collect(Collectors.toList());
//        if (CollUtils.isEmpty(availableCoupons)){
//            return CollUtils.emptyList();
//        }
//        log.debug("经过初筛之后，还是 {}张",availableCoupons.size());
//        for (Coupon coupon : availableCoupons) {
//            log.debug("优惠券: {}, {}",
//                    DiscountStrategy.getDiscount(coupon.getDiscountType()).getRule(coupon),
//                    coupon);
//        }
//        //3.细筛（需要考虑优惠券的限定范围）排列组合
//        //3.1查询每一张优惠券的限定范围，找出可用课程 存到 Map<优惠券，可用课程>
//        Map<Coupon, List<OrderCourseDTO>> avaMap = findAvailableCoupons(availableCoupons,courses);
//        if (avaMap.isEmpty()){
//            return CollUtils.emptyList(); //所有优惠券都不可用
//        }
//
//        Set<Map.Entry<Coupon, List<OrderCourseDTO>>> entries = avaMap.entrySet();
//        for (Map.Entry<Coupon, List<OrderCourseDTO>> entry : entries) {
//                log.debug("细筛之后优惠券: {}, {}",
//                        DiscountStrategy.getDiscount(entry.getKey().getDiscountType()).getRule(entry.getKey()),
//                        entry.getKey());
//            List<OrderCourseDTO> value = entry.getValue();
//            for (OrderCourseDTO courseDTO : value) {
//                log.debug("可用课程：{}",courseDTO);
//            }
//        }
//        availableCoupons = new ArrayList<>(avaMap.keySet()); //过滤掉不可用的优惠券，真正可用的优惠券集合
//        log.debug("经过细篩之后，还是 {}张",availableCoupons.size());
//        for (Coupon coupon : availableCoupons) {
//            log.debug("优惠券: {}, {}",
//                    DiscountStrategy.getDiscount(coupon.getDiscountType()).getRule(coupon),
//                    coupon);
//        }
//
//        List<List<Coupon>> solutions = PermuteUtil.permute(availableCoupons);
//        for (Coupon availableCoupon : availableCoupons) {
//            solutions.add(List.of(availableCoupon)); //添加单券到方案中，前端不仅展示最佳方案，还要不方案里面的券罗列出来
//        }
//        log.debug("排列组合----------");
//        for (List<Coupon> solution : solutions) {
//            List<Long> cids = solution.stream().map(Coupon::getId).collect(Collectors.toList());
//            log.debug("优惠券组合：{}",cids);
//        }
//        //4.计算每一种组合的优惠明细
//        /*log.debug("开始计算 每一种组合的优惠明细------");
//        List<CouponDiscountDTO> dtos = new ArrayList<>();
//        for (List<Coupon> solution : solutions) {
//            CouponDiscountDTO dto = calculateSolutionDiscount(avaMap,courses,solution);
//            log.debug("方案最终优惠 {}  方案中优惠券使用了 {} 规则 {}",dto.getDiscountAmount(),dto.getIds(),dto.getRules());
//            dtos.add(dto);
//        }*/
//
//        //5.使用多线程改造第4步，并行计算每一种组合的优惠明细
//        log.debug("多线程计算  每一种组合的优惠明细");
////        List<CouponDiscountDTO> dtos = new ArrayList<>(); //线程不安全
//        List<CouponDiscountDTO> dtos = Collections.synchronizedList(new ArrayList<>(solutions.size()));//线程安全的集合
//        CountDownLatch latch = new CountDownLatch(solutions.size());
//        for (List<Coupon> solution : solutions) {
//            CompletableFuture.supplyAsync(new Supplier<CouponDiscountDTO>() {
//                @Override
//                public CouponDiscountDTO get() {
//                    CouponDiscountDTO dto = calculateSolutionDiscount(avaMap,courses,solution);
//                    return dto;
//                }
//            },calculateSolutionExecutor).thenAccept(new Consumer<CouponDiscountDTO>() {
//                @Override
//                public void accept(CouponDiscountDTO dto) {
//                    log.debug("方案最终优惠 {}  方案中优惠券使用了 {} 规则 {}",dto.getDiscountAmount(),dto.getIds(),dto.getRules());
//                    dtos.add(dto);
//                    latch.countDown();//计数器减1
//                }
//            });
//        }
//        try {
//            latch.await(2, TimeUnit.SECONDS);//主线程最多阻塞2秒，如果2秒内没有完成，则返回
//        }catch (InterruptedException e){
//            log.error("多线程计算组合优惠明细 报错了",e);
//        }
//
//        //6.筛选最优解
//        return findBestSolution(dtos);
//    }
//
//    /**
//     * 筛选最优方案,求最优解
//     * - 用券相同时，优惠金额最高的方案
//     * - 优惠金额相同时，用券最少的方案
//     * @param soultions
//     * @return
//     */
//    private List<CouponDiscountDTO> findBestSolution(List<CouponDiscountDTO> soultions) {
//        //1.创建两个 map 分别记录用券相同，金额最高  金额相同，用券最少
//        Map<String ,CouponDiscountDTO> moreDiscountMap = new HashMap<>();//用券相同，金额最高
//        Map<Integer,CouponDiscountDTO> lessCouponMap = new HashMap<>();//金额相同，用券最少
//
//        //2.循环方案 向map中记录 用券相同，金额最高 ，金额相同，用券最少
//        for (CouponDiscountDTO soultion : soultions) {
//            //2.1对优惠券id 升序，转字符串 然后以逗号拼接
//            String ids = soultion.getIds().stream()
//                    .sorted(Comparator.comparing(Long::longValue))
//                    .map(String::valueOf)
//                    .collect(Collectors.joining(","));
//            //2.2从moreDiscountMap中取 旧的记录 判断 如果旧的方案金额 大于等于 当前方案的优惠金额 当前方案忽略 处理下一个方案
//            CouponDiscountDTO old = moreDiscountMap.get(ids);
//            if (old != null && old.getDiscountAmount() >= soultion.getDiscountAmount()){
//                continue;
//            }
//            //2.3从lessCouponMap中取 旧的记录 判断 如果旧的方案优惠券数量 小于等于 当前方案的优惠券数量 当前方案忽略 处理下一个方案
//            old = lessCouponMap.get(soultion.getDiscountAmount());
//            int newSize = soultion.getIds().size();//当前方案的优惠券数量
//            if (old != null && old.getIds().size() <= newSize && newSize >1){ //单券的就不比较了，只比较组合的
//                continue;
//            }
//            //2.4添加更优方案到map中
//            moreDiscountMap.put(ids,soultion);//说明当前方案 更优
//            lessCouponMap.put(soultion.getDiscountAmount(),soultion);//说明当前方案 更优
//        }
//
//        //3.求两个map的交集
//        Collection<CouponDiscountDTO> bestSolution = CollUtils.intersection(moreDiscountMap.values(), lessCouponMap.values());
//        //4.对最终的方案结果 按优惠金额 倒序
//        List<CouponDiscountDTO> latestBestSolution = bestSolution.stream()
//                .sorted(Comparator.comparing(CouponDiscountDTO::getDiscountAmount).reversed())
//                .collect(Collectors.toList());
//        return latestBestSolution;
//    }
//
//    /**
//     * 计算每一个方案的 优惠信息
//     * @param avaMap Map<优惠券，可用课程>
//     * @param courses 订单中的课程
//     * @param solution 某一方案
//     * @return
//     */
//    private CouponDiscountDTO calculateSolutionDiscount(Map<Coupon, List<OrderCourseDTO>> avaMap,
//                                                        List<OrderCourseDTO> courses,
//                                                        List<Coupon> solution) {
//        // 1.创建方案结果dto对象
//        CouponDiscountDTO dto = new CouponDiscountDTO();
//        // 2.初始化商品id和商品折扣明细的映射，初始折扣明细全都设置为 0 设置map结构，key为商品id，value初始值为 0
//        Map<Long, Integer> detailMap = courses.stream().collect(Collectors.toMap(OrderCourseDTO::getId, orderCourseDTO -> 0));
//        // 3.计算该方案的优惠信息
//        // 3.1循环方案中优惠券
//        for (Coupon coupon : solution) {
//            // 3.2取出该优惠券对应的可用课程
//            List<OrderCourseDTO> availableCourse = avaMap.get(coupon);
//            // 3.3计算可用课程的总金额（商品价格 - 该商品的折扣明细）（优惠后的价格）
//            int totalAmount = availableCourse.stream()
//                    .mapToInt(value -> value.getPrice() - detailMap.get(value.getId())).sum();
//            // 3.4判断优惠券是否可用
//            Discount discount = DiscountStrategy.getDiscount(coupon.getDiscountType());
//            if (!discount.canUse(totalAmount, coupon)) {
//                continue; //券不可用，跳出循环，继续处理下一个券
//            }
//            // 3.5计算该优惠券使用后的折扣值
//            int discountAmount = discount.calculateDiscount(totalAmount, coupon);
//            // 3.6计算商品的折扣明细  更新到detailMap中
//            calculateDetailDiscount(detailMap,availableCourse,totalAmount,discountAmount);
//            // 3.7累加每一个优惠券的优惠金额  赋值给方案结果dto对象
//            dto.getIds().add(coupon.getId());//只要执行的这句话，就意味着这个优惠券生效了
//            dto.getRules().add(discount.getRule(coupon));
//            dto.setDiscountAmount(discountAmount + dto.getDiscountAmount());//不能覆盖，应该是所有生效的优惠券累加的结果
//        }
//        return dto;
//    }
//
//    /**
//     * 计算商品的 折扣明细
//     * @param detailMap  商品id和商品的优惠明细 映射
//     * @param availableCourse 当前优惠券可用的课程集合
//     * @param totalAmount  可用的课程的总金额
//     * @param discountAmount  当前优惠券的折扣金额
//     */
//    private void calculateDetailDiscount(Map<Long, Integer> detailMap,
//                                         List<OrderCourseDTO> availableCourse,
//                                         int totalAmount,
//                                         int discountAmount) {
//        //目的：本方法就是优惠券在使用后  计算每个商品的折扣明细
//        //规则：目前的商品按比例计算，最后一个商品折扣明细 = 总的优惠金额 - 前面商品优惠的总额
//        //循环可用商品
//        int times = 0;//代表已处理的商品个数
//        int remainDiscount = discountAmount;//代表剩余的优惠金额
//
//        for (OrderCourseDTO c : availableCourse) {
//            times++;
//            int discount = 0; //当前商品的折扣明细
//            if (times == availableCourse.size()){//如果是最后一个商品
//                //计算最后一个商品的折扣明细
//                discount = remainDiscount;
//            }else{//不是最后一个商品
//                //计算当前商品的折扣明细
//                discount = c.getPrice() * discountAmount / totalAmount;//先乘 再除 否则为 0
//                remainDiscount = remainDiscount - discount;
//            }
//            //将商品的折扣明细添加到 detailMap中 累加
//            detailMap.put(c.getId(),discount + detailMap.get(c.getId()));
//        }
//    }
//
//    /**
//     * 细筛：查询每一张优惠券的限定范围，找出可用课程 存到 Map<优惠券，可用课程>
//     * @param coupons 初筛之后的优惠券集合
//     * @param orderCourses 订单中的课程集合
//     * @return Map<优惠券，可用课程>
//     */
//    private Map<Coupon, List<OrderCourseDTO>> findAvailableCoupons(List<Coupon> coupons,
//                                                                   List<OrderCourseDTO> orderCourses) {
//        Map<Coupon, List<OrderCourseDTO>> map = new HashMap<>();
//        //1.循环遍历初筛后的优惠券集合
//        for (Coupon coupon : coupons) {
//            //2.找出每一个优惠券的可用课程
//            List<OrderCourseDTO> availableCourses = orderCourses;//如果没有限定范围，则最后保存的还是原始课程
//            //2.1判断优惠券是否限定了范围  coupon.specific为true
//            if (coupon.getSpecific()){
//                //2.2查询限定范围.查询coupon_scope表，条件coupon_id
//                List<CouponScope> scopeList = couponScopeService.lambdaQuery()
//                        .eq(CouponScope::getCouponId, coupon.getId())
//                        .list();
//                //2.3得到限定范围的id集合
//                List<Long> scopeIds = scopeList.stream().map(CouponScope::getBizId).collect(Collectors.toList());
//                //2.4从 orderCourses 订单中所有的课程集合筛选 该范围内的课程
//                availableCourses = orderCourses.stream()
//                        .filter(orderCourseDTO -> scopeIds.contains(orderCourseDTO.getCateId()))
//                        .collect(Collectors.toList());
//            }
//            if (CollUtils.isEmpty(availableCourses)){
//                continue;//说明当前优惠券限定了范围，但是在订单中的课程中没有找到可用课程，说明该卷不可用，忽略该卷，进行下一个优惠券的处理
//            }
//            //3.计算该优惠券可用课程的总金额
//            int totalAmount = availableCourses.stream().mapToInt(OrderCourseDTO::getPrice).sum();//代表可用课程的总金额
//            //4.判断该优惠券是否可用如果可用添加到map中
//            Discount discount = DiscountStrategy.getDiscount(coupon.getDiscountType());
//            if (discount.canUse(totalAmount,coupon)){
//                map.put(coupon,availableCourses);
//            }
//        }
//        return map;
//    }
//
//    //保存优惠券
//    private void saveUserCoupon(Long userId, Coupon coupon) {
//        // 1.基本信息
//        UserCoupon uc = new UserCoupon();
//        uc.setUserId(userId);
//        uc.setCouponId(coupon.getId());
//        // 2.有效期信息
//        LocalDateTime termBeginTime = coupon.getTermBeginTime(); //优惠券使用 开始时间
//        LocalDateTime termEndTime = coupon.getTermEndTime(); //优惠券使用 结束时间
//        if (termBeginTime == null) {
//            termBeginTime = LocalDateTime.now();
//            termEndTime = termBeginTime.plusDays(coupon.getTermDays());
//        }
//        uc.setTermBeginTime(termBeginTime);
//        uc.setTermEndTime(termEndTime);
//        // 3.保存
//        save(uc);
//    }
//}
